<template>
  <view class="container">
    <button @click="chooseImage">选择图片</button>
    <view v-if="tempImagePath" class="image-container">
      <image :src="tempImagePath" mode="aspectFit"></image>
    </view>
    <view v-if="showCropper" class="cropper-mask">
      <view class="cropper-container">
        <image :src="tempImagePath" mode="widthFix" class="crop-image" ref="cropImage"></image>
        <view class="crop-frame" ref="cropFrame"></view>
        <view class="cropper-buttons">
          <button @click="cancelCrop">取消</button>
          <button @click="confirmCrop">确认裁剪</button>
        </view>
      </view>
    </view>
    <view v-if="croppedImagePath" class="result-container">
      <text>裁剪结果:</text>
      <image :src="croppedImagePath" mode="aspectFit"></image>
      <button @click="saveImageToServer">保存到服务器</button>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      tempImagePath: '',
      croppedImagePath: '',
      showCropper: false,
      cropRect: {
        x: 0,
        y: 0,
        width: 200,
        height: 200
      },
      imageInfo: {},
      touchStart: { x: 0, y: 0 },
      isDragging: false,
      dragType: '',
      originalRect: {},
      uploadProgress: 0,
      isUploading: false
    }
  },
  methods: {
    // 选择图片
    chooseImage() {
      uni.chooseImage({
        count: 1,
        sourceType: ['album', 'camera'],
        success: (res) => {
          this.tempImagePath = res.tempFilePaths[0];
          this.getImageInfo();
        },
        fail: (err) => {
          console.error('选择图片失败', err);
        }
      });
    },
    
    // 获取图片信息
    getImageInfo() {
      uni.getImageInfo({
        src: this.tempImagePath,
        success: (info) => {
          this.imageInfo = info;
          this.showCropper = true;
          this.$nextTick(() => {
            this.initCropFrame();
            this.initTouchEvents();
          });
        },
        fail: (err) => {
          console.error('获取图片信息失败', err);
        }
      });
    },
    
    // 初始化裁剪框
    initCropFrame() {
      const query = uni.createSelectorQuery().in(this);
      query.select('.crop-image').boundingClientRect((rect) => {
        if (rect) {
          this.cropRect.width = Math.min(rect.width * 0.8, 300);
          this.cropRect.height = this.cropRect.width;
          this.cropRect.x = (rect.width - this.cropRect.width) / 2;
          this.cropRect.y = (rect.height - this.cropRect.height) / 2;
        }
      }).exec();
    },
    
    // 初始化触摸事件
    initTouchEvents() {
      const cropFrame = this.$refs.cropFrame;
      if (!cropFrame) return;
      
      cropFrame.addEventListener('touchstart', (e) => {
        const touch = e.touches[0];
        this.touchStart = { x: touch.clientX, y: touch.clientY };
        this.originalRect = { ...this.cropRect };
        
        const { x, y, width, height } = this.cropRect;
        const padding = 20;
        
        if (Math.abs(touch.clientX - x) < padding && Math.abs(touch.clientY - y) < padding) {
          this.dragType = 'resize-tl';
        } else if (Math.abs(touch.clientX - (x + width)) < padding && Math.abs(touch.clientY - y) < padding) {
          this.dragType = 'resize-tr';
        } else if (Math.abs(touch.clientX - x) < padding && Math.abs(touch.clientY - (y + height)) < padding) {
          this.dragType = 'resize-bl';
        } else if (Math.abs(touch.clientX - (x + width)) < padding && Math.abs(touch.clientY - (y + height)) < padding) {
          this.dragType = 'resize-br';
        } else if (touch.clientX >= x && touch.clientX <= x + width && 
                  touch.clientY >= y && touch.clientY <= y + height) {
          this.dragType = 'move';
        } else {
          this.dragType = '';
          return;
        }
        
        this.isDragging = true;
      });
      
      cropFrame.addEventListener('touchmove', (e) => {
        if (!this.isDragging) return;
        
        const touch = e.touches[0];
        const dx = touch.clientX - this.touchStart.x;
        const dy = touch.clientY - this.touchStart.y;
        
        const query = uni.createSelectorQuery().in(this);
        query.select('.crop-image').boundingClientRect((rect) => {
          if (!rect) return;
          
          if (this.dragType === 'move') {
            let newX = this.originalRect.x + dx;
            let newY = this.originalRect.y + dy;
            
            newX = Math.max(0, Math.min(newX, rect.width - this.cropRect.width));
            newY = Math.max(0, Math.min(newY, rect.height - this.cropRect.height));
            
            this.cropRect.x = newX;
            this.cropRect.y = newY;
          } else if (this.dragType.includes('resize')) {
            let newWidth = this.originalRect.width;
            let newHeight = this.originalRect.height;
            let newX = this.originalRect.x;
            let newY = this.originalRect.y;
            
            if (this.dragType === 'resize-tl') {
              newWidth = Math.max(50, this.originalRect.width - dx);
              newHeight = Math.max(50, this.originalRect.height - dy);
              newX = this.originalRect.x + dx;
              newY = this.originalRect.y + dy;
            } else if (this.dragType === 'resize-tr') {
              newWidth = Math.max(50, this.originalRect.width + dx);
              newHeight = Math.max(50, this.originalRect.height - dy);
              newY = this.originalRect.y + dy;
            } else if (this.dragType === 'resize-bl') {
              newWidth = Math.max(50, this.originalRect.width - dx);
              newHeight = Math.max(50, this.originalRect.height + dy);
              newX = this.originalRect.x + dx;
            } else if (this.dragType === 'resize-br') {
              newWidth = Math.max(50, this.originalRect.width + dx);
              newHeight = Math.max(50, this.originalRect.height + dy);
            }
            
            if (newX < 0) {
              newWidth += newX;
              newX = 0;
            }
            if (newY < 0) {
              newHeight += newY;
              newY = 0;
            }
            if (newX + newWidth > rect.width) {
              newWidth = rect.width - newX;
            }
            if (newY + newHeight > rect.height) {
              newHeight = rect.height - newY;
            }
            
            newWidth = Math.max(50, newWidth);
            newHeight = Math.max(50, newHeight);
            
            this.cropRect = { x: newX, y: newY, width: newWidth, height: newHeight };
          }
        }).exec();
      });
      
      cropFrame.addEventListener('touchend', () => {
        this.isDragging = false;
        this.dragType = '';
      });
    },
    
    // 取消裁剪
    cancelCrop() {
      this.showCropper = false;
    },
    
    // 确认裁剪
    confirmCrop() {
      const { x, y, width, height } = this.cropRect;
      const { width: imgWidth, height: imgHeight } = this.imageInfo;
      
      const query = uni.createSelectorQuery().in(this);
      query.select('.crop-image').boundingClientRect((rect) => {
        if (!rect) return;
        
        const scaleX = imgWidth / rect.width;
        const scaleY = imgHeight / rect.height;
        
        const originalX = Math.floor(x * scaleX);
        const originalY = Math.floor(y * scaleY);
        const originalWidth = Math.floor(width * scaleX);
        const originalHeight = Math.floor(height * scaleY);
        
        const ctx = uni.createCanvasContext('cropCanvas');
        
        ctx.drawImage(this.tempImagePath, 0, 0, imgWidth, imgHeight);
        
        ctx.draw(false, () => {
          uni.canvasToTempFilePath({
            canvasId: 'cropCanvas',
            x: originalX,
            y: originalY,
            width: originalWidth,
            height: originalHeight,
            destWidth: originalWidth,
            destHeight: originalHeight,
            fileType: 'jpg',
            quality: 0.8,
            success: (res) => {
              this.croppedImagePath = res.tempFilePath;
              this.showCropper = false;
            },
            fail: (err) => {
              console.error('裁剪图片失败', err);
              uni.showToast({
                title: '裁剪失败，请重试',
                icon: 'none'
              });
            }
          });
        });
      }).exec();
    },
    
    // 保存图片到服务器
    saveImageToServer() {
      if (!this.croppedImagePath) return;
      
      this.isUploading = true;
      this.uploadProgress = 0;
      
      const uploadTask = uni.uploadFile({
        url: 'https://your-api-domain.com/upload/image', // 替换为实际的上传API地址
        filePath: this.croppedImagePath,
        name: 'image',
        formData: {
          'user_id': uni.getStorageSync('user_id') || '', // 可选：如果需要用户ID
          'type': 'avatar' // 可选：上传类型
        },
        success: (res) => {
          if (res.statusCode === 200) {
            try {
              const result = JSON.parse(res.data);
              if (result.code === 0) {
                uni.showToast({
                  title: '上传成功',
                  icon: 'success'
                });
                this.$emit('imageUploaded', result.data);
              } else {
                uni.showToast({
                  title: result.message || '上传失败',
                  icon: 'none'
                });
              }
            } catch (e) {
              console.error('解析上传结果失败', e);
              uni.showToast({
                title: '上传失败，服务器响应异常',
                icon: 'none'
              });
            }
          } else {
            uni.showToast({
              title: `上传失败，状态码: ${res.statusCode}`,
              icon: 'none'
            });
          }
        },
        fail: (err) => {
          console.error('上传失败', err);
          uni.showToast({
            title: '上传失败，请检查网络连接',
            icon: 'none'
          });
        },
        complete: () => {
          this.isUploading = false;
        }
      });
      
      // 监听上传进度
      uploadTask.onProgressUpdate((res) => {
        this.uploadProgress = res.progress;
        console.log('上传进度：' + res.progress);
        console.log('已经上传的数据长度：' + res.totalBytesSent);
        console.log('预期需要上传的数据总长度：' + res.totalBytesExpectedToSend);
      });
    }
  },
  beforeDestroy() {
    const cropFrame = this.$refs.cropFrame;
    if (cropFrame) {
      cropFrame.removeEventListener('touchstart');
      cropFrame.removeEventListener('touchmove');
      cropFrame.removeEventListener('touchend');
    }
  }
}
</script>

<style>
.container {
  padding: 20rpx;
}

.image-container {
  margin-top: 20rpx;
  width: 100%;
  height: 400rpx;
  background-color: #f5f5f5;
  display: flex;
  justify-content: center;
  align-items: center;
}

.cropper-mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.7);
  z-index: 999;
  display: flex;
  justify-content: center;
  align-items: center;
}

.cropper-container {
  position: relative;
  width: 90%;
  max-height: 80%;
  background-color: #fff;
  border-radius: 10rpx;
  overflow: hidden;
}

.crop-image {
  max-width: 100%;
}

.crop-frame {
  position: absolute;
  border: 2rpx dashed #fff;
  box-shadow: 0 0 0 1000rpx rgba(0, 0, 0, 0.5);
  
 &::before, &::after,
  &:before, &:after {
    content: '';
    position: absolute;
    width: 12rpx;
    height: 12rpx;
    background-color: #fff;
    border-radius: 50%;
    box-shadow: 0 0 2rpx rgba(0,0,0,0.3);
  }
  
  &::before {
    top: -6rpx;
    left: -6rpx;
    cursor: nwse-resize;
  }
  
  &::after {
    top: -6rpx;
    right: -6rpx;
    cursor: nesw-resize;
  }
  
  &:before {
    bottom: -6rpx;
    left: -6rpx;
    cursor: nesw-resize;
  }
  
  &:after {
    bottom: -6rpx;
    right: -6rpx;
    cursor: nwse-resize;
  }
}

.cropper-buttons {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  justify-content: space-around;
  padding: 20rpx;
  background-color: #fff;
}

.result-container {
  margin-top: 20rpx;
}

.upload-progress {
  margin-top: 10rpx;
  width: 100%;
  height: 10rpx;
  background-color: #f3f3f3;
  border-radius: 5rpx;
  overflow: hidden;
}
  .progress-bar {
    height: 100%;
    background-color: #007aff;
    transition: width 0.3s ease;
  }
</style>    